import datetime
import pytz
import decimal
from base64 import b64encode, b64decode
from json import JSONEncoder
from string import ascii_letters, digits
from random import sample
from typing import Union, List, Dict
from chinese_calendar import is_workday

import orjson
from dateutil.relativedelta import relativedelta
from dateutil.parser import parse, ParserError
from core.logger import logger


class JsonDataEncoder(JSONEncoder):
    """
    any类型转换为string|float输出json
    """

    def default(self, obj: Union[Dict, List]) -> Union[Union[str, float]]:
        """
        :param obj:  dict|list
        :return:     str|float
        """
        if isinstance(obj, datetime.datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')
        if isinstance(obj, datetime.date):
            return obj.strftime('%Y-%m-%d')
        if isinstance(obj, decimal.Decimal):
            return float(obj)
        # if isinstance(obj, bytes):
        #     return str(obj, encoding='utf-8')
        return JSONEncoder.default(self, obj)


def to_str(byte_or_str):
    """
    :param byte_or_str: 字节或字符串
    :return: 总是返回string类型
    """
    if isinstance(byte_or_str, bytes):
        ret = byte_or_str.decode('utf-8')
    else:
        ret = byte_or_str
    return ret


def to_byte(byte_or_str):
    """
    :param byte_or_str: 字节或字符串
    :return: 总是返回byte类型
    """
    if isinstance(byte_or_str, str):
        ret = byte_or_str.encode('utf-8')
    else:
        ret = byte_or_str
    return ret


def to_b64encode(data):
    """
    :param data: base64加密数据
    :return:
    """
    if isinstance(data, str):
        return to_str(b64encode(data.encode("utf-8")))
    else:
        return False


def to_b64decode(data):
    """
    :param data:
    :return:
    """
    if isinstance(data, bytes):
        return str(b64decode(data.decode("utf-8")))
    elif isinstance(data, str):
        data = to_byte(data)
        try:
            return str(b64decode(data), "utf-8")
        except Exception as e:
            return False


def is_json(data):
    """
    :param data: 数据
    :return: bool: True|False 判断合法json数据
    """
    try:
        orjson.loads(data)
    except (ValueError, TypeError) as e:
        return False
    return True


def is_empty(data):
    """
    :param data: 数据
    :return: bool: True|False 判断数据是否為空（為空返回True,非空返回False）
    """
    if data is None:
        return True
    if not data:
        return True
    if isinstance(data, str) and data.replace(" ", "") == "":
        return True
    return False


def random_generator(min_length=12):
    """
    :param min_length: int 随机密码长度
    :return: str 随机密码
    """
    return ''.join(sample(ascii_letters + digits, min_length))


def cst_to_utc(cst_datetime: Union[datetime.datetime, datetime.date, str]):
    """
    :param cst_datetime: 世界协调时间+8:00
    :return: 世界协调时间+0:00
    """
    utc_datetime = (parse(cst_datetime) - relativedelta(hours=8)).strftime("%Y-%m-%dT%H:%M:%S,000Z")
    return utc_datetime


def utc_to_cst(utc_datetime: Union[datetime.datetime, datetime.date, str]):
    """
    :param utc_datetime: 世界协调时间+0:00
    :return: 世界协调时间+8:00
    """
    cst_datetime = (parse(utc_datetime) + relativedelta(hours=8)).strftime("%Y-%m-%dT%H:%M:%S,000Z")
    return cst_datetime


def get_month_range(date_month: str):
    this_month = parse(date_month)
    next_month = this_month + relativedelta(months=1)
    first_day = (datetime.date(this_month.year, this_month.month, 1)).strftime('%Y-%m-%d')
    last_day = (datetime.date(next_month.year, next_month.month, 1) - relativedelta(days=1)).strftime('%Y-%m-%d')

    return first_day, last_day


class CommonTimeUtils():
    """  通用的时间处理操作
    """

    def __init__(self):
        pass

    @classmethod
    def strptime(cls, time_data, utc=False):
        """
        字符串转datetime类型
        :param time_data:
        :param utc:
        :return:
        """
        if isinstance(time_data, datetime.datetime):
            return time_data
        
        if time_data == None or time_data == "" or time_data.startswith("000"):
            return None

        if "T" in time_data:
            dt = datetime.datetime.strptime(time_data, "%Y-%m-%dT%H:%M:%S.%fZ")
        else:
            dt = datetime.datetime.strptime(time_data, "%Y-%m-%d %H:%M:%S")
            
        if not utc:
            return dt

        local_tz = pytz.timezone("Asia/Shanghai")
        local_dt = dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
        return local_dt



    @classmethod
    def strftime(cls, time_data):
        """
        datetime类型转字符串
        :param time_data:
        :return:
        """
        if time_data == None:
            return ""
        return datetime.datetime.strftime(time_data, "%Y-%m-%d %H:%M:%S")



    @classmethod
    def check_now_is_work_time(cls):
        try:
            now_time = datetime.datetime.now()
            if is_workday(now_time):
                if CommonTimeUtils.check_between_every_day(datetime.time(9,30), datetime.time(12,0)) or \
                    CommonTimeUtils.check_between_every_day(datetime.time(13,30), datetime.time(18,30)):
                    return True
        except Exception as e:
            logger.error(f"check_now_is_work_time error => {e}")
            return False
        return False



    @classmethod
    def check_between_every_day(cls, start_time: datetime.time, end_time: datetime.time):
        """
        判断现在的时间是否在配置的区间内（只有：时分秒）
        """
        now_time = datetime.datetime.now().time()

        if start_time > end_time:
            # 跨天的情况，例如： 晚上23：30 到早上8：30
            if start_time <= now_time or now_time <= end_time:
                return True
        else:
            # 没有跨天，例如： 早上8：30 到下午18：30
            if start_time <= now_time and now_time <= end_time:
                return True
        # 没有在区间内
        return False



    @classmethod
    def check_between_datetime(cls, start_datetime: datetime.datetime, end_datetime: datetime.datetime):
        """
        判断现在的时间是否在配置的区间内（有：年月日-时分秒）
        """
        now_time = datetime.datetime.now()
        if start_datetime <= now_time and now_time <= end_datetime:
            return True

        return False



    @classmethod
    def get_timestamp_oneday(self, date_info: str = None):
        """
        获取一个日期的时间戳范围：开始和结束
        """
        if not date_info:
            # 默认为昨天
            datetime_info = datetime.datetime.now() - datetime.timedelta(days=1)
        else:
            datetime_info = datetime.datetime.strptime(date_info, "%Y-%m-%d")

        start_timestamp = datetime_info.replace(hour=0, minute=0, second=0, microsecond=0).timestamp()
        end_timestamp = datetime_info.replace(hour=23, minute=59, second=59, microsecond=0).timestamp()

        return start_timestamp, end_timestamp



def main():
    # CommonTimeUtils.check_now_is_work_time()
    first_day, last_day = get_month_range("2023-07")
    print(first_day, last_day)

    
    
    
if __name__ == "__main__":
    main()